#include "DXUT.h" // this include has to be included in the beginning of each source file for compiling

#include "./../Headers/constant_buffer.h"

namespace ShaderSpace {

	ConstantBuffer::ConstantBuffer()
		: value(NULL), buffer_gpu(NULL), slider_timer(NULL) {

		if( ! CreatePointerAllocation( value ) )
			throw Exception( "Couldn't allocate memmory!" );

		if( ! CreatePointerAllocation( buffer_gpu ) )
			throw Exception( "Couldn't allocate memmory!" );

	};

	ConstantBuffer::ConstantBuffer( const ConstantBuffer &original ) {
	};

	ConstantBuffer::~ConstantBuffer(){
		this->destroy();

		SafeDelete( value );
		SafeDelete( buffer_gpu );
	};

	void ConstantBuffer::startValue( const usint number_of_points, const usint number_of_frames, const CModelViewerCamera &camera, const float &tesselation_factor ){
		value->mViewProjection = *camera.GetProjMatrix();
		value->vCameraPosWorld = *camera.GetEyePt();

		value->fTessellationFactor = tesselation_factor;

		value->iTimer = 0;
		value->fInterpolationTimer = 0.0;
		value->iLimitTime = number_of_frames;

		value->iQttControlPoints = number_of_points;

		value->fRadius = 0.75;

		value->iInterpolationType = 0;

	};

	double modular( const double &value, const double &base ) {
		if( base == 0.0 )
			throw DivisionByZeroException<double>( "Cannot modulate with base zero", value );
		return value - ( Low( value / base ) * base );
	};

	void ConstantBuffer::setTimeValue( const double &time ) {
		value->iTimer = ( value->iLimitTime + Low(time) ) % value->iLimitTime;
		value->fInterpolationTimer = (float) Fract( time );
		if( slider_timer != NULL ){

			int min_val, max_val;
			slider_timer->GetRange( min_val, max_val );
			slider_timer->SetValue( min_val + Low( ( value->iTimer + value->fInterpolationTimer ) * 10 ) );
			if( label_timer != NULL ){

				WCHAR wstring[100];
				swprintf_s( wstring, L"Time: %2.1f", slider_timer->GetValue() / 10.0 );
				label_timer->SetText( wstring );

			}

		}
	};

	void ConstantBuffer::updateValue( const CModelViewerCamera &camera, const double &elapsed_time , const float &tesselation_factor ){
		// WVP
		D3DXMATRIX mViewProjection;
		D3DXMATRIX mProj = *camera.GetProjMatrix();
		D3DXMATRIX mView = *camera.GetViewMatrix();

		mViewProjection = mView * mProj;

		D3DXMatrixTranspose( &value->mViewProjection, &mViewProjection );
		value->vCameraPosWorld = *( camera.GetEyePt() );
		value->fTessellationFactor = tesselation_factor;
		updateTimeValue( elapsed_time );
		//value->iLimitTime = limitTime;
		//value->iQttControlPoints = qttPt;
		//value->iInterpolationType = 0;

	};

	bool ConstantBuffer::updateOnDeviceContext( ID3D11DeviceContext *target_device_context ){
		// Update per-frame variables
		D3D11_MAPPED_SUBRESOURCE MappedResource;
		target_device_context->Map( getBufferGPU() , 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource );
		CB_PER_FRAME_CONSTANTS* pData = ( CB_PER_FRAME_CONSTANTS* )MappedResource.pData;

		*pData = *value;

		target_device_context->Unmap( getBufferGPU(), 0 );

		return true;
	};

	bool ConstantBuffer::createOnDevice( ID3D11Device *target_device ){

		D3D11_BUFFER_DESC descriptor;
		descriptor.Usage = D3D11_USAGE_DYNAMIC;
		descriptor.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		descriptor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
//		descriptor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		descriptor.MiscFlags = 0;
//		descriptor.ByteWidth = 96; // TODO - don't know why, but don't change the 96 value until figure it out
		descriptor.ByteWidth = 112;
//		descriptor.ByteWidth = 12 + sizeof(CB_PER_FRAME_CONSTANTS); // TODO - don't know why, 12 is always added.
		//descriptor.ByteWidth = sizeof( CB_PER_FRAME_CONSTANTS );

		D3D11_SUBRESOURCE_DATA initial_data;
		ZeroMemory( &initial_data, sizeof(initial_data) );
		initial_data.pSysMem = value;

		// validating descriptor and initial data
		if( FAILED( target_device->CreateBuffer( &descriptor, &initial_data, NULL ) ) )
			throw Exception( "Wrong parameters" );

		if( FAILED( target_device->CreateBuffer( &descriptor, &initial_data, buffer_gpu ) ) )
			return false; // problem while allocating the buffer_gpu

		return true;
	};

	bool ConstantBuffer::destroy(){
		SAFE_RELEASE( *buffer_gpu );
		return true;
	};

};
